<template>
  <view class="relation-graph-container">
    <view class="relation-map">
      <view class="relation-graph-content">
        <!-- 快捷筛选图例放最上方 -->
        <view class="relation-legend">
          <view
              v-for="type in chartRelationTypes"
              :key="type.type"
              class="legend-item"
              :class="{ active: selectedRelations.includes(type.type) }"
              @tap="toggleRelation(type.type)"
          >
            <view class="legend-dot" :style="{ background: type.color }"></view>
            <text class="legend-label">{{ type.label }}</text>
          </view>
        </view>
        <l-echart
          :key="chartKey"
          ref="chartRef"
          :height="'100%'"
          :width="'100%'"
          unit="px"
          :style="{ width: '100%', height: '100%', background: graphStyle.backgroundColor }"
          @ready="onChartReady"
        ></l-echart>
        <!-- 关系数据为空时的提示 -->
        <view v-if="!chartLoading && (!apiNodes.length || !apiEdges.length)" class="empty-data-tip">
          <view class="empty-icon">🔗</view>
          <view class="empty-title">暂无关系数据</view>
          <view class="empty-desc">请先添加联系人并维护关系，构建您的人脉网络</view>
          <button class="empty-action-btn" @tap="showRelationMaintain">立即维护关系</button>
        </view>



        <view v-if="chartLoading" class="chart-loading-mask">
          <text>加载中...</text>
        </view>

        <view class="bottom-actions">
          <button class="action-btn" @tap="saveLayout">保存布局</button>
          <button class="action-btn" @tap="showRelationMaintain">维护关系</button>
        </view>
      </view>
    </view>
    <!-- 悬浮操作区 -->
    <graph-actions
      :chart-instance="chartInstance"
      :chart-ref="$refs.chartRef"
      :zoom-level="zoomLevel"
      @filter="showFilterDrawer = true"
      @zoom-in="zoomLevel = $event"
      @zoom-out="zoomLevel = $event"
      @refresh="refresh"
      @save-image="onSaveImage"
      @settings="showSettingDrawer = true"
      @update:zoom-level="zoomLevel = $event"
    />
    
    <!-- 节点详细信息弹窗 -->
    <NodeDetailPopup 
      :show="showNodeDetail" 
      :node-info="nodeDetailInfo"
      @close="showNodeDetail = false"
    />
    <!-- 筛选抽屉 -->
    <FilterDrawer 
      :show="showFilterDrawer" 
      :filter-options="filterOptions"
      v-model:filter-values="filterValues"
      @close="showFilterDrawer = false"
      @apply="handleFilterApply"
      @reset="handleFilterReset"
    />
    <!-- 设置抽屉 -->
    <SettingDrawer 
      :show="showSettingDrawer" 
      v-model:setting="setting"
      :color-palette="colorPalette"
      :line-width-options="lineWidthOptions"
      @close="showSettingDrawer = false"
      @apply="handleSettingApply"
    />
  </view>
</template>

<script>
import { ref } from 'vue';
import limeEchart from '@/uni_modules/lime-echart/components/lime-echart/lime-echart.vue';
import SuPopup from '@/sheep/ui/su-popup/su-popup.vue';
import UniDataPicker from '@/uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue';
import RelationApi from '@/sheep/api/contact/relation';
import CommApi from '@/sheep/api/comm/comm';
import GraphActions from './graph-actions.vue';
import SettingDrawer from './setting-drawer.vue';
import FilterDrawer from './filter-drawer.vue';
import NodeDetailPopup from './node-detail-popup.vue';
import safeStorage from '@/sheep/util/storage';

const chartRef = ref(null)

// #ifdef H5
import * as echarts from 'echarts';
// #endif
// #ifdef MP
const echarts = require('../../../uni_modules/lime-echart/static/echarts.min.js');
// #endif

export default {
  name: 'RelationGraph',
  components: {
    limeEchart,
    SuPopup,
    UniDataPicker,
    GraphActions,
    SettingDrawer,
    FilterDrawer,
    NodeDetailPopup
  },
  /**
   * 组件参数props说明：
   * @param {Array} nodes 关系图节点数据，格式[{id, type, ...}]
   * @param {Array} edges 关系图连线数据，格式[{source, target, type, ...}]
   * @param {Array} relationTypes 关系类型，格式[{type, label, color}]
   * @param {Object} nodeConfig 节点配置项 { size, color, contentFields }
   * @param {Object} edgeConfig 连线配置项 { color, width, type, randomColor }
   * @param {Object} popupFields 节点弹窗内容配置 { person: [...], company: [...] }
   * @param {Array} filterOptions 筛选项配置 [{ key, label, type, options }]
   * @param {Object} graphStyle 关系图整体样式 { backgroundColor }
   */
  props: {
    nodes: { type: Array, default: () => [] },
    edges: { type: Array, default: () => [] },
    relationTypes: { type: Array, default: () => [] },
    nodeConfig: { type: Object, default: () => ({ size: 40, color: '', contentFields: ['text', 'phone', 'company', 'address'] }) },
    edgeConfig: { type: Object, default: () => ({ color: '', width: 2, type: 'solid', randomColor: true }) },

    filterOptions: { type: Array, default: () => ([
      { key: 'company', label: '企业', type: 'select', options: [] },
      { key: 'region', label: '地区', type: 'select', options: [] },
      { key: 'gender', label: '性别', type: 'select', options: [ { label: '男', value: 'male' }, { label: '女', value: 'female' } ] },
      { key: 'age', label: '年龄', type: 'range', options: [] }
    ]) },
    graphStyle: { type: Object, default: () => ({ backgroundColor: '#e1eaff' }) }
  },
      data() {
    return {

      showNodeDetail: false,
      nodeDetailInfo: null,
      selectedRelations: [],
      chartInstance: null,
      retryCount: 0,
      maxRetries: 3,
      zoomLevel: 0.8,
      chartLoading: true,
      chartKey: 0,
      chartHeight: 400,
      apiNodes: this.nodes && this.nodes.length ? this.nodes : [],
      apiEdges: this.edges && this.edges.length ? this.edges : [],
      apiRelationTypes: this.relationTypes && this.relationTypes.length ? this.relationTypes : [],
      layoutMode: 'force',
      lastUpdateTime: 0, // 记录最后更新时间
      hasExistingLayout: false, // 标记是否有现有布局数据
      dragIntervals: [], // 存储拖拽相关的定时器
      lastCenterTime: 0, // 记录最后居中时间
      
      // 性能优化相关状态
      lastSaveTime: 0, // 记录最后保存时间
      isDragInProgress: false, // 标记是否正在拖拽
      pendingSaveLayoutTimer: null, // 待保存布局的定时器
      pendingUpdateTimer: null, // 待更新图表的定时器
      
      // 初始化状态管理
      isInitializing: true, // 标记是否正在初始化
      hasFetchedData: false, // 标记是否已获取过数据
      initializationComplete: false, // 标记初始化是否完成
      showFilterDrawer: false,
      showSettingDrawer: false,
      filterValues: {},
      setting: {
        nodeSize: this.nodeConfig.size || 40,
        nodeColor: this.nodeConfig.color || '#8B5CF6',
        edgeWidth: this.edgeConfig.width || 2,
        backgroundColor: this.graphStyle.backgroundColor || '#e1eaff'
      },
      colorPalette: [
        '#FFFFFF','#000000','#FF5722','#FF9800','#FFEB3B','#4ADE80','#22D3EE',
        '#2563EB','#A21CAF','#FCA5A5','#F59E42','#FDE68A','#6EE7B7','#5EEAD4'
      ],
      lineWidthOptions: [
        { label: '细', value: 1 },
        { label: '略细', value: 2 },
        { label: '中等', value: 3 },
        { label: '略粗', value: 4 },
        { label: '粗', value: 6 }
      ],

    }
  },
  computed: {
    relationTypesMap() {
      const map = {};
      (this.apiRelationTypes || []).forEach(item => {
        map[item.type] = item;
      });
      return map;
    },
    chartNodes() {
      return this.apiNodes.length > 0 ? this.apiNodes : [];
    },
    chartEdges() {
      return this.apiEdges.length > 0 ? this.apiEdges : [];
    },
    chartRelationTypes() {
      return this.apiRelationTypes.length > 0 ? this.apiRelationTypes : [];
    },
    chartOption() {
      const nodes = this.chartNodes;
      const edges = this.chartEdges;
      const relationTypes = this.chartRelationTypes;

      // 为确保边的有效性，创建节点ID的Set以供快速查找
      const nodeIds = new Set(nodes.map(n => String(n.id)));

      // 确定要显示的关系类型
      const selectedTypes = this.selectedRelations && this.selectedRelations.length > 0
          ? this.selectedRelations
          : relationTypes.map(t => t.type);

      // 过滤边：确保其类型被选中，且源节点和目标节点都存在
      const filteredEdges = edges.filter(edge => {
        const typeMatched = selectedTypes.includes(edge.type);
        const sourceExists = nodeIds.has(String(edge.source));
        const targetExists = nodeIds.has(String(edge.target));
        return typeMatched && sourceExists && targetExists;
      });

      // 收集有连线的节点ID
      const connectedNodeIds = new Set();
      filteredEdges.forEach(edge => {
        connectedNodeIds.add(String(edge.source));
        connectedNodeIds.add(String(edge.target));
      });

      // 过滤节点：只显示有连线的节点
      const filteredNodes = nodes.filter(node =>
          connectedNodeIds.has(String(node.id))
      );

      const finalNodes = filteredNodes.map(node => {
        const nodeSize = this.setting?.nodeSize || this.nodeConfig.size || (node.type === 'company' ? 50 : 40);
        
        const nodeConfig = {
          ...node,
          id: String(node.id),
          draggable: true,
          symbolSize: nodeSize,
          cursor: 'pointer',
          category: node.type === 'company' ? 0 : 1,
          // 固定使用头像+姓名模式
          symbol: this.getNodeSymbol(node),
          itemStyle: {
            color: this.setting?.nodeColor || this.nodeConfig.color || (node.type === 'company' ? '#10B981' : '#8B5CF6'),
            borderWidth: 0,
            shadowBlur: 6,
            shadowColor: 'rgba(0, 0, 0, 0.15)'
          }
        };

        // 固定配置：头像在节点中，姓名在节点下方
        const hasAvatar = node.avatarUrl && node.avatarUrl.trim() !== '';
        const nodeName = node.name || node.text;
        
        if (hasAvatar) {
          // 有头像时，标记需要异步处理头像符号
          nodeConfig._needAvatarSymbol = true;
          nodeConfig._avatarUrl = node.avatarUrl;
          nodeConfig._nodeSize = nodeSize;
          
          // 下方显示完整名称（色块标签边框样式）
          nodeConfig.label = {
            show: nodeName ? true : false,
            position: 'bottom',
            formatter: nodeName || '',
            fontSize: Math.max(10, Math.min(13, nodeSize / 4)),
            color: '#fff',
            fontWeight: 'bold',
            distance: 5,
            backgroundColor: '#4285f4',
            padding: [3, 8],
            borderRadius: 12,
            shadowBlur: 4,
            shadowColor: 'rgba(66, 133, 244, 0.3)'
          };
        } else {
          // 没有头像时，节点内显示完整姓名
          nodeConfig.label = {
            show: nodeName ? true : false,
            position: 'inside',
            formatter: nodeName || '',
            fontSize: Math.max(10, Math.min(14, nodeSize / 3)),
            color: '#fff',
            fontWeight: 'bold'
          };
        }

        // 在none布局下，必须提供有效的坐标
        if (this.layoutMode === 'none') {
          nodeConfig.x = typeof node.x === 'number' ? node.x : 0;
          nodeConfig.y = typeof node.y === 'number' ? node.y : 0;
          nodeConfig.fixed = true; // 标记为固定位置
        }

        return nodeConfig;
      });

      // 检查 layoutMode 为 'none' 时节点是否都有坐标
      if (this.layoutMode === 'none') {
        const hasInvalidCoords = finalNodes.some(node =>
            typeof node.x !== 'number' || typeof node.y !== 'number' ||
            isNaN(node.x) || isNaN(node.y)
        );

        if (hasInvalidCoords) {
          // 强制切换为 force 布局，并清空坐标
          this.layoutMode = 'force';
          finalNodes.forEach(node => {
            node.x = null;
            node.y = null;
          });
        }
      }

      const option = {
        backgroundColor: this.setting?.backgroundColor || this.graphStyle.backgroundColor || 'transparent',
        tooltip: { 
          show: false
        },
        // 性能优化：在拖拽过程中禁用动画
        animation: !this.isDragInProgress,
        animationDuration: this.isDragInProgress ? 0 : (this.layoutMode === 'none' ? 0 : 800), // 减少动画时长
        animationDurationUpdate: this.isDragInProgress ? 0 : (this.layoutMode === 'none' ? 0 : 800),
        animationEasingUpdate: 'cubicOut', // 使用更快的缓动函数
        series: [{
          type: 'graph',
          layout: this.layoutMode,
          draggable: true,
          symbolSize: this.setting?.nodeSize || this.nodeConfig.size || 40,
          roam: true,
          // 🎯 优化力导向布局参数，提升性能
          force: {
            repulsion: 600,     // 适中的斥力
            edgeLength: 180,    // 适中的边长
            gravity: 0.2,      // 适中的重力
            friction: 0.6,     // 增加摩擦力，减少震荡
            layoutAnimation: !this.isDragInProgress // 拖拽时禁用布局动画
          },
          // 🎯 设置图形的显示区域，确保不超出屏幕和正确居中
          center: ['50%', '50%'],  // 图形居中
          zoom: this.zoomLevel || 0.8,   // 使用动态缩放级别
          // 强制居中的额外设置
          left: 'center',
          top: 'middle',
          right: null,
          bottom: null,
          scaleLimit: {           // 缩放限制
            min: 0.3,
            max: 3
          },
          // 确保边的配置正确应用
          edgeSymbol: ['none', 'arrow'],
          edgeSymbolSize: [0, 6], // 减小箭头大小
          // 🎯 使用autoCurveness自动处理多重关系，确保连线不重合
          autoCurveness: true,
          data: finalNodes,
          // 使用 links 而不是 edges（按照官方样例）
          links: this.processMultipleEdges(filteredEdges),
          categories: [
            { name: 'company', itemStyle: { color: this.setting?.nodeColor || this.nodeConfig.color || '#10B981' } },
            { name: 'person', itemStyle: { color: this.setting?.nodeColor || this.nodeConfig.color || '#8B5CF6' } }
          ],
          // 在series级别设置默认lineStyle，不设置curveness让autoCurveness处理
          lineStyle: {
            opacity: 0.8, // 略微降低透明度减少渲染负担
            width: this.setting?.edgeWidth || this.edgeConfig.width || 2,
            color: '#8B5CF6',
            type: this.edgeConfig.type || 'solid'
            // 移除curveness设置，完全交给autoCurveness自动处理
          },
          emphasis: {
            focus: 'adjacency',
            lineStyle: { width: 3 }, // 减小强调时的线宽
            itemStyle: { 
              shadowBlur: this.isDragInProgress ? 0 : 15, // 拖拽时禁用阴影
              shadowColor: 'rgba(59, 130, 246, 0.3)' 
            }
          }
        }]
      };

      return option;
    }
  },
  watch: {
    apiNodes(val) {
      // 拖拽时延迟更新
      if (this.isDragInProgress) {
        this.scheduleDeferredUpdate();
      } else {
      this.updateChart();
      }
    },
    apiEdges(val) {
      // 拖拽时延迟更新
      if (this.isDragInProgress) {
        this.scheduleDeferredUpdate();
      } else {
      this.updateChart();
      }
    },
    apiRelationTypes(val) {
      if (val.length > 0) {
        this.selectedRelations = val.map(t => t.type);
      }
      // 拖拽时延迟更新
      if (this.isDragInProgress) {
        this.scheduleDeferredUpdate();
      } else {
      this.updateChart();
      }
    },
    selectedRelations: {
      handler(val, oldVal) {
        // 拖拽时延迟更新
        if (this.isDragInProgress) {
          this.scheduleDeferredUpdate();
        } else {
        this.updateChart();
        }
      },
      deep: true // 深度监听数组变化
    },
    layoutMode(newVal) {
      // 布局模式变化必须立即更新
      this.updateChart();
    },
    // 外部props变更时自动同步data
    nodes: {
      handler(val) {
        this.apiNodes = val && val.length ? val : [];
      },
      deep: true
    },
    edges: {
      handler(val) {
        this.apiEdges = val && val.length ? val : [];
      },
      deep: true
    },
    relationTypes: {
      handler(val) {
        this.apiRelationTypes = val && val.length ? val : [];
      },
      deep: true
    },
    nodeConfig: {
      handler(val) {
        if (val) this.setting.nodeSize = val.size || 40;
        if (val) this.setting.nodeColor = val.color || '#8B5CF6';
      },
      deep: true
    },
    edgeConfig: {
      handler(val) {
        if (val) this.setting.edgeWidth = val.width || 2;
      },
      deep: true
    },
    graphStyle: {
      handler(val) {
        if (val) this.setting.backgroundColor = val.backgroundColor || '#e1eaff';
      },
      deep: true
    },
  },
  methods: {
    // 性能优化工具函数
    throttle(func, limit) {
      let lastExecution = 0;
      return (...args) => {
        const now = Date.now();
        if (now - lastExecution >= limit) {
          lastExecution = now;
          return func.apply(this, args);
        }
      };
    },

    debounce(func, delay) {
      let timeoutId;
      return (...args) => {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => func.apply(this, args), delay);
      };
    },

    // 节流版本的保存布局函数 - 最多每2秒保存一次
    throttledSaveLayout() {
      const now = Date.now();
      if (now - this.lastSaveTime >= 2000) {
        this.lastSaveTime = now;
        this.saveLayoutToLocal();
      } else {
        // 如果频繁调用，使用延迟保存
        this.scheduleDeferredSave();
      }
    },

    // 延迟保存布局
    scheduleDeferredSave() {
      if (this.pendingSaveLayoutTimer) {
        clearTimeout(this.pendingSaveLayoutTimer);
      }
      this.pendingSaveLayoutTimer = setTimeout(() => {
        this.saveLayoutToLocal();
        this.lastSaveTime = Date.now();
      }, 1000);
    },

    // 延迟更新图表
    scheduleDeferredUpdate() {
      if (this.pendingUpdateTimer) {
        clearTimeout(this.pendingUpdateTimer);
      }
      this.pendingUpdateTimer = setTimeout(() => {
        if (!this.isDragInProgress) {
          this.updateChart();
        }
      }, 300);
    },

    async updateChart() {
      // 如果正在拖拽，延迟更新避免卡顿
      if (this.isDragInProgress) {
        console.log('拖拽进行中，延迟图表更新');
        this.scheduleDeferredUpdate();
        return;
      }

      const chartComponent = this.$refs.chartRef;
      if (!chartComponent) {
        console.log('图表组件未找到');
        return;
      }

      // 应用筛选条件
      let filteredNodes = this.apiNodes;
      let filteredEdges = this.apiEdges;
      if (this.filterValues && Object.keys(this.filterValues).length > 0) {
        filteredNodes = filteredNodes.filter(node => {
          let pass = true;
          if (this.filterValues.company && node.company !== undefined && node.company !== this.filterValues.company) pass = false;
          if (this.filterValues.region && node.region !== undefined && node.region !== this.filterValues.region) pass = false;
          if (this.filterValues.gender && node.gender !== undefined && node.gender !== this.filterValues.gender) pass = false;
          if (this.filterValues.age && node.age !== undefined) {
            if (typeof this.filterValues.age === 'object' && this.filterValues.age.length === 2) {
              if (node.age < this.filterValues.age[0] || node.age > this.filterValues.age[1]) pass = false;
            } else if (node.age !== this.filterValues.age) {
              pass = false;
            }
          }
          return pass;
        });
        const nodeIds = new Set(filteredNodes.map(n => n.id));
        filteredEdges = filteredEdges.filter(edge => nodeIds.has(edge.source) && nodeIds.has(edge.target));
      }
      // 用筛选后的数据渲染
      this.apiNodes = filteredNodes;
      this.apiEdges = filteredEdges;

      // 在更新图表之前，处理需要异步创建头像符号的节点
      await this.processAvatarSymbols();

      // 如果图表实例已存在，直接更新选项
      if (this.chartInstance) {
        try {
          // 检查实例是否已被销毁
          if (this.chartInstance.isDisposed && this.chartInstance.isDisposed()) {
            console.log('图表实例已被销毁，重新初始化');
            this.chartInstance = null;
          } else {
            // 性能优化：仅在非拖拽状态下进行完整更新
            this.chartInstance.setOption(this.chartOption, true);
          }
        } catch (error) {
          console.log('图表实例操作失败，可能已被销毁:', error);
          this.chartInstance = null;
        }

        // 只有在成功更新选项后才进行自动适配和返回
        if (this.chartInstance) {
          // 🎯 更新后进行自动适配和居中 - 优化版本
          if (!this.isDragInProgress && this.initializationComplete) {
            // 避免在初始化期间频繁调用
          setTimeout(() => {
            if (this.chartInstance && typeof this.chartInstance.getOption === 'function') {
              this.autoFitScreen(this.chartInstance);
            }
          }, 100);
          }

          return;
        }
      }

      // #ifdef H5
      import('echarts').then(echarts => {
        chartComponent.init(echarts).then((chart) => {
          console.log('H5端初始化图表');
          chart.setOption(this.chartOption);
          this.chartInstance = chart;
          this.chartLoading = false;

          // 验证图表实例和缩放功能
          console.log('图表初始化完成，验证缩放功能:', {
            hasDispatchAction: typeof chart.dispatchAction === 'function',
            hasSetOption: typeof chart.setOption === 'function',
            isDisposed: chart.isDisposed ? chart.isDisposed() : false
          });

          // 添加双击事件监听 - 优先绑定
          this.addNodeClickEvents(chart);

          // 添加拖拽事件监听
          this.addDragEvents(chart);

          // 🎯 图表初始化后进行一次性适配和居中
          if (this.initializationComplete) {
          this.autoFitScreen(chart);
          } else {
            // 初始化期间只进行简单居中
          setTimeout(() => {
              console.log('初始化完成，执行适配和居中(H5)');
            this.centerChart(chart);
              this.initializationComplete = true;
            }, 1000);
          }
        }).catch(error => {
          console.error('H5端图表初始化失败:', error);
          this.chartLoading = false;
        });
      });
      // #endif
      // #ifdef MP
      if (chartComponent.init) {
        chartComponent.init(echarts).then(chart => {
          console.log('小程序端初始化图表');
          chart.setOption(this.chartOption);
          this.chartInstance = chart;
          this.chartLoading = false;

          // 验证图表实例和缩放功能
          console.log('图表初始化完成，验证缩放功能:', {
            hasDispatchAction: typeof chart.dispatchAction === 'function',
            hasSetOption: typeof chart.setOption === 'function',
            isDisposed: chart.isDisposed ? chart.isDisposed() : false
          });

          // 添加双击事件监听 - 优先绑定
          this.addNodeClickEvents(chart);

          // 添加拖拽事件监听
          this.addDragEvents(chart);

          // 🎯 图表初始化后进行一次性适配和居中
          if (this.initializationComplete) {
          this.autoFitScreen(chart);
          } else {
            // 初始化期间只进行简单居中
          setTimeout(() => {
              console.log('初始化完成，执行适配和居中(小程序)');
            this.centerChart(chart);
              this.initializationComplete = true;
            }, 1000);
          }
        }).catch(error => {
          console.error('小程序端图表初始化失败:', error);
          this.chartLoading = false;
        });
      }
      // #endif
    },
    handleNodeDblClick(params) {
      console.log('双击事件处理开始:', params);
      if (params.dataType === 'node') {
        const node = this.apiNodes.find(n => n.id === params.data.id)
        console.log('找到的节点数据:', node);
        if (node) {
          // 显示节点详细信息弹窗
          this.nodeDetailInfo = node;
          this.showNodeDetail = true;
          console.log('✅ 节点详情弹窗已显示, showNodeDetail:', this.showNodeDetail);
          console.log('✅ 节点信息:', this.nodeDetailInfo);
        } else {
          console.error('❌ 未找到对应的节点数据, nodeId:', params.data.id);
        }
      } else {
        console.log('❌ 双击的不是节点:', params.dataType);
      }
    },

    // 添加节点点击和双击事件监听
    addNodeClickEvents(chart) {
      if (!chart) return;

      console.log('添加节点点击事件监听');

      // 手动双击检测变量
      let clickState = {
        lastClickTime: 0,
        lastClickNodeId: null,
        clickTimer: null
      };

      // 使用单击事件手动检测双击
      chart.on('click', (params) => {
        console.log('ECharts单击事件触发:', params);
        
        if (params.dataType === 'node') {
          const currentTime = Date.now();
          const nodeId = params.data.id;
          
          // 检查是否为双击（300ms内点击同一节点）
          if (clickState.lastClickTime && 
              currentTime - clickState.lastClickTime < 300 && 
              clickState.lastClickNodeId === nodeId) {
            
            // 这是双击
            console.log('检测到手动双击:', params);
            this.handleNodeDblClick(params);
            
            // 清除单击定时器
            if (clickState.clickTimer) {
              clearTimeout(clickState.clickTimer);
              clickState.clickTimer = null;
            }
            
            // 重置状态
            clickState.lastClickTime = 0;
            clickState.lastClickNodeId = null;
            
          } else {
            // 这是第一次点击，设置定时器等待可能的双击
            clickState.lastClickTime = currentTime;
            clickState.lastClickNodeId = nodeId;
            
            // 清除之前的定时器
            if (clickState.clickTimer) {
              clearTimeout(clickState.clickTimer);
            }
            
            // 设置单击定时器（如果300ms内没有第二次点击，则处理为单击）
            clickState.clickTimer = setTimeout(() => {
              console.log('确认为单击事件:', params);
              // 这里可以添加单击的处理逻辑
              clickState.lastClickTime = 0;
              clickState.lastClickNodeId = null;
              clickState.clickTimer = null;
            }, 300);
          }
        }
      });

      // 仍然保留原生双击事件作为备用
      chart.on('dblclick', (params) => {
        console.log('ECharts原生双击事件触发:', params);
        this.handleNodeDblClick(params);
      });
    },

    // 添加拖拽事件监听 - 优化版本
    addDragEvents(chart) {
      if (!chart) return;

      console.log('添加优化后的拖拽事件监听');

      // 标记拖拽状态
      let dragState = {
        isDragging: false,
        nodeId: null,
        startTime: 0,
        startPosition: null,
        hasMoved: false
      };

      // 创建节流版本的mousemove处理函数 - 每16ms最多执行一次（60fps）
      const throttledMouseMove = this.throttle((params) => {
        if (dragState.nodeId && params.event?.event?.buttons === 1) {
          if (!dragState.hasMoved && dragState.startPosition) {
            // 简化距离计算 - 使用曼哈顿距离而不是欧几里得距离
            const currentPos = { x: params.event.offsetX, y: params.event.offsetY };
            const deltaX = Math.abs(currentPos.x - dragState.startPosition.x);
            const deltaY = Math.abs(currentPos.y - dragState.startPosition.y);

            if (deltaX > 8 || deltaY > 8) { // 移动超过8px才算真正开始拖拽
              dragState.hasMoved = true;
              if (!dragState.isDragging) {
                dragState.isDragging = true;
                this.isDragInProgress = true;
                console.log('开始拖拽节点:', dragState.nodeId);
              }
            }
          }
        }
      }, 16);

      // 监听鼠标按下
      chart.on('mousedown', (params) => {
        if (params.dataType === 'node') {
          // 优化拖拽初始化 - 减少延迟，提高响应性
          setTimeout(() => {
            dragState.nodeId = params.data.id;
            dragState.startTime = Date.now();
            dragState.hasMoved = false;
            dragState.startPosition = { x: params.event?.offsetX, y: params.event?.offsetY };
          }, 200); // 减少延迟到200ms
        }
      });

      // 使用节流版本的鼠标移动监听
      chart.on('mousemove', throttledMouseMove);

      // 监听鼠标松开
      chart.on('mouseup', (params) => {
        if (dragState.isDragging && dragState.hasMoved) {
          console.log('拖拽结束，使用节流保存布局，节点ID:', dragState.nodeId);

          // 使用节流版本的保存布局
          this.throttledSaveLayout();
        }

        // 重置拖拽状态
        this.resetDragState(dragState);
        this.isDragInProgress = false;
      });

      // 优化全局鼠标松开监听
      chart.getZr().on('mouseup', () => {
        if (dragState.isDragging && dragState.hasMoved) {
          this.throttledSaveLayout();
        }
        this.resetDragState(dragState);
        this.isDragInProgress = false;
      });

      // 简化图形事件监听
      chart.on('graphRoam', (params) => {
        if (dragState.nodeId && !dragState.isDragging) {
            dragState.isDragging = true;
            dragState.hasMoved = true;
          this.isDragInProgress = true;
        }
      });

      // 移除定期检查的定时器，减少CPU占用
      // 改为依赖事件驱动的方式处理拖拽结束
    },

    // 重置拖拽状态
    resetDragState(dragState) {
      dragState.isDragging = false;
      dragState.nodeId = null;
      dragState.startTime = 0;
      dragState.startPosition = null;
      dragState.hasMoved = false;
    },

    // 自动切换到none布局（仅在首次进入且无布局数据时）
    autoSwitchToNoneLayout(chart) {
      if (!chart || this.layoutMode !== 'force' || this.hasExistingLayout) {
        // 静默跳过，不需要日志输出
        return;
      }

      console.log('检测到首次进入，将在4秒后自动切换到none布局');

      // 等待force布局稳定后自动切换为none布局
      setTimeout(() => {
        if (this.layoutMode === 'force') {
          console.log('Force布局完成，自动切换为none布局');

          // 获取当前节点位置
          const graph = chart.getModel().getSeriesByIndex(0).getGraph();
          const currentNodes = graph.nodes.map(node => {
            const model = node.getModel();
            const [x, y] = node.getLayout();
            return {
              ...model.option,
              x: Math.round(x),
              y: Math.round(y)
            };
          });

          // 更新数据中的坐标
          this.apiNodes = this.apiNodes.map(node => {
            const currentNode = currentNodes.find(n => n.id == node.id);
            return currentNode ? { ...node, x: currentNode.x, y: currentNode.y } : node;
          });

          // 切换布局模式
          this.layoutMode = 'none';

          // 重新渲染整个图表，确保none布局生效
          this.$nextTick(() => {
            chart.setOption(this.chartOption, true); // 使用true强制更新
            console.log('图表已重新渲染为none布局');

            // 自动保存当前布局
            setTimeout(() => {
              this.saveLayoutToLocal();
            }, 200);
          });
        }
      }, 4000); // 延长到4秒，确保force布局完全稳定
    },

    // 处理节点拖拽结束事件
    handleNodeDragEnd(params) {
      if (params.dataType === 'node' && this.chartInstance) {
        console.log('节点拖拽结束，保存布局');
        // 用户拖拽节点后，自动保存到本地存储
        setTimeout(() => {
          this.saveLayoutToLocal();
        }, 100); // 稍微延迟确保位置已更新
      }
    },

    // 保存布局到本地存储
    saveLayoutToLocal() {
      if (!this.chartInstance) {
        console.log('图表实例不存在，无法保存布局');
        return;
      }

      try {
        const series = this.chartInstance.getModel().getSeriesByIndex(0);
        if (!series) {
          console.log('图表系列不存在，无法保存布局');
          return;
        }

        const graph = series.getGraph();
        if (!graph || !graph.nodes) {
          console.log('图表数据不存在，无法保存布局');
          return;
        }

        const nodes = graph.nodes.map(node => {
          const model = node.getModel();
          const [x, y] = node.getLayout();
          const nodeData = {
            ...model.option,
            x: Math.round(x), // 四舍五入避免小数
            y: Math.round(y)
          };
          return nodeData;
        });

        const layoutJson = JSON.stringify(nodes);
        safeStorage.setItem('graphLayout', layoutJson);

        // 立即更新 apiNodes 的坐标，确保后续操作不会丢失布局
        this.updateApiNodesWithCurrentPositions(nodes);

        // 验证保存是否成功
        const saved = safeStorage.getItem('graphLayout');
      } catch (error) {
        console.log('保存布局到本地失败:', error);
      }
    },
    processMultipleEdges(edges) {
      // 🎯 优化版本：缓存配置，减少重复计算
      const edgeWidth = this.setting?.edgeWidth || this.edgeConfig.width || 2;
      const edgeType = this.edgeConfig.type || 'solid';
      
      // 预定义颜色数组，避免随机计算
      const defaultColors = ['#8B5CF6', '#10B981', '#F59E0B', '#EF4444', '#3B82F6', '#8B5A2B'];
      let colorIndex = 0;
      
      return edges.map(edge => {
        const relType = this.relationTypesMap[edge.type] || {};
        
        // 优化颜色分配：使用预定义颜色或类型颜色
        let color = relType.color || defaultColors[colorIndex++ % defaultColors.length];
        
        // 简化的link配置，减少DOM复杂度
        const linkConfig = {
          source: String(edge.source),
          target: String(edge.target),
          value: relType.label || edge.name || '',
          lineStyle: {
            color,
            width: edgeWidth,
            opacity: 0.8,
            type: edgeType
          },
          type: edge.type
        };

        // 根据拖拽状态决定是否显示标签，拖拽时隐藏提升性能
        if (!this.isDragInProgress) {
          linkConfig.label = {
            show: true,
            formatter: relType.label || edge.name || '',
            fontSize: 12,        // 减小字体提升性能
            fontWeight: 'normal', // 减少渲染负担
            color: '#555',       
            backgroundColor: 'rgba(255,255,255,0.85)', 
            padding: [2, 6],     // 减小内边距
            borderRadius: 4,     
            borderWidth: 0,      // 移除边框减少渲染
            shadowBlur: 0,       // 移除阴影提升性能
          };
        } else {
          // 拖拽时完全隐藏标签
          linkConfig.label = { show: false };
        }

        return linkConfig;
      });
    },

    // 🎯 自动适配图表到屏幕大小 - 优化版本
    autoFitScreen(chart) {
      if (!chart || typeof chart.getOption !== 'function') {
        console.log('图表实例无效，跳过自动适配');
        return;
      }

      // 防止初始化期间频繁调用
      if (this.isInitializing && !this.initializationComplete) {
        console.log('初始化期间，跳过自动适配');
        return;
      }

      // 简化的适配逻辑，减少定时器数量
      setTimeout(() => {
        try {
          console.log('执行自动适配和居中，布局模式:', this.layoutMode);
          
          // 先执行自动适配
              this.performAutoFit(chart);
          
          // 统一延迟居中，避免多次调用
          setTimeout(() => {
            this.centerChart(chart);
          }, this.layoutMode === 'force' ? 1000 : 200);
          
        } catch (error) {
          console.log('自动适配屏幕失败:', error);
        }
      }, this.layoutMode === 'force' ? 1500 : 300);
    },

    // 🎯 图表居中方法
    centerChart(chart) {
      if (!chart || typeof chart.dispatchAction !== 'function') {
        console.log('图表实例无效，跳过居中');
        return;
      }

      // 防止频繁居中 - 增加间隔时间
      const now = Date.now();
      if (this.lastCenterTime && now - this.lastCenterTime < 3000) {
        // 降低日志级别，减少控制台噪音
        if (now - this.lastCenterTime < 1000) {
          // 1秒内的调用静默跳过
          return;
        }
        console.log('距离上次居中时间太短，跳过本次居中');
        return;
      }
      this.lastCenterTime = now;

      try {
        console.log('执行图表居中，当前布局模式:', this.layoutMode);
        
        if (this.layoutMode === 'none') {
          // none布局模式下需要特殊处理
          this.centerNoneLayoutChart(chart);
        } else {
          // force布局模式下使用标准方法
          this.centerForceLayoutChart(chart);
        }

      } catch (error) {
        console.log('图表居中失败:', error);
      }
    },

    // 处理none布局的居中
    centerNoneLayoutChart(chart) {
      console.log('处理none布局的居中');
      
      // 获取容器尺寸
      const containerWidth = chart.getWidth() || 750;
      const containerHeight = chart.getHeight() || 400;
      
      // 计算图形中心应该在的位置
      const targetCenterX = containerWidth / 2;
      const targetCenterY = containerHeight / 2;
      
      // 获取当前图形的边界
      const option = chart.getOption();
      if (option.series && option.series[0] && option.series[0].data) {
        const nodes = option.series[0].data;
        let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
        
        nodes.forEach(node => {
          if (typeof node.x === 'number' && typeof node.y === 'number') {
            minX = Math.min(minX, node.x);
            maxX = Math.max(maxX, node.x);
            minY = Math.min(minY, node.y);
            maxY = Math.max(maxY, node.y);
          }
        });
        
        if (isFinite(minX) && isFinite(maxX) && isFinite(minY) && isFinite(maxY)) {
          // 计算当前图形的中心
          const currentCenterX = (minX + maxX) / 2;
          const currentCenterY = (minY + maxY) / 2;
          
          // 计算需要偏移的距离
          const offsetX = targetCenterX - currentCenterX;
          const offsetY = targetCenterY - currentCenterY;
          
          console.log('计算居中偏移:', { offsetX, offsetY, targetCenterX, targetCenterY, currentCenterX, currentCenterY });
          
          // ✅ 直接修改节点坐标来实现居中（none布局下最有效的方法）
          if (Math.abs(offsetX) > 5 || Math.abs(offsetY) > 5) {
            const updatedNodes = nodes.map(node => {
              if (typeof node.x === 'number' && typeof node.y === 'number') {
                return {
                  ...node,
                  x: node.x + offsetX,
                  y: node.y + offsetY
                };
              }
              return node;
            });
            
            console.log('应用节点坐标偏移:', { offsetX, offsetY, 节点数量: updatedNodes.length });
            
            // 更新图表数据
            chart.setOption({
              series: [{
                data: updatedNodes
              }]
            }, false);
            
                         // 同步更新本地数据
             this.updateApiNodesCoordinates(offsetX, offsetY);
             
             // 居中后自动保存布局，下次加载时就是居中的
             setTimeout(() => {
               this.saveLayoutToLocal();
               console.log('居中后已保存布局');
             }, 100);
           } else {
             console.log('偏移量太小，无需调整');
           }
        }
      }
    },

    // 更新本地节点坐标数据
    updateApiNodesCoordinates(offsetX, offsetY) {
      this.apiNodes = this.apiNodes.map(node => {
        if (typeof node.x === 'number' && typeof node.y === 'number') {
          return {
            ...node,
            x: node.x + offsetX,
            y: node.y + offsetY
          };
        }
        return node;
      });
      console.log('已同步更新本地节点坐标');
    },

    // 统一的组件初始化方法
    async initializeComponent() {
      if (this.hasFetchedData) {
        console.log('组件已初始化，跳过重复初始化');
        return;
      }

      console.log('开始初始化关系图组件...');
      this.isInitializing = true;
      
      // 设置初始更新时间，避免意外的refresh触发
      this.lastUpdateTime = Date.now();
      
      try {
        // 获取图表数据
        await this.fetchGraphData();
        this.hasFetchedData = true;
        
        // 等待数据加载完成后再初始化图表
        if (!this.chartLoading) {
          await this.updateChart();
        }
        
        this.initializationComplete = true;
        console.log('关系图组件初始化完成');
      } catch (error) {
        console.error('初始化关系图组件失败:', error);
      } finally {
        this.isInitializing = false;
      }
    },

    // 清理所有定时器和缓存的方法
    clearAllTimers() {
      // 清理拖拽相关定时器
      if (this.dragIntervals && this.dragIntervals.length > 0) {
        this.dragIntervals.forEach(interval => {
          if (interval) {
            clearInterval(interval);
          }
        });
        this.dragIntervals = [];
      }

      // 清理延迟保存定时器
      if (this.pendingSaveLayoutTimer) {
        clearTimeout(this.pendingSaveLayoutTimer);
        this.pendingSaveLayoutTimer = null;
      }

      // 清理延迟更新定时器
      if (this.pendingUpdateTimer) {
        clearTimeout(this.pendingUpdateTimer);
        this.pendingUpdateTimer = null;
      }

      // 清理头像缓存，释放内存
      if (this.avatarCache) {
        this.avatarCache.clear();
        this.avatarCache = null;
      }

      // 重置所有状态
      this.isDragInProgress = false;
      this.lastSaveTime = 0;
      this.lastCenterTime = 0;
      this.isInitializing = false;
      this.hasFetchedData = false;
      this.initializationComplete = false;
    },

    // 处理force布局的居中
    centerForceLayoutChart(chart) {
      console.log('处理force布局的居中');
      
      // 方法1：重置视图到初始状态
      chart.dispatchAction({
        type: 'restore'
      });

      // 方法2：设置图形居中并适应容器
      setTimeout(() => {
        // 获取容器大小
        const width = chart.getWidth() || 750;
        const height = chart.getHeight() || 400;
        
        chart.setOption({
          series: [{
            center: [width/2, height/2],
            zoom: this.zoomLevel || 0.8
          }]
        }, false);
        
        console.log('force布局设置绝对居中坐标:', { centerX: width/2, centerY: height/2, zoom: this.zoomLevel || 0.8 });
      }, 100);

      // 方法3：使用数据视图重置（最后的保障）
      setTimeout(() => {
        chart.dispatchAction({
          type: 'dataZoom',
          start: 0,
          end: 100
        });
      }, 200);
    },

    // 执行自动适配
    performAutoFit(chart) {
      try {
        // 检查chart是否存在且未被销毁
        if (!chart || typeof chart.getOption !== 'function') {
          console.log('图表实例无效，跳过自动适配');
          return;
        }

        // 检查实例是否已被销毁
        if (chart.isDisposed && chart.isDisposed()) {
          console.log('图表实例已被销毁，跳过自动适配');
          return;
        }

        // 获取图表的数据范围
        const option = chart.getOption();
        if (!option.series || !option.series[0] || !option.series[0].data) {
          return;
        }

        const nodes = option.series[0].data;
        if (nodes.length === 0) return;

        // 计算节点的边界
        let minX = Infinity, maxX = -Infinity;
        let minY = Infinity, maxY = -Infinity;

        nodes.forEach(node => {
          if (typeof node.x === 'number' && typeof node.y === 'number') {
            minX = Math.min(minX, node.x);
            maxX = Math.max(maxX, node.x);
            minY = Math.min(minY, node.y);
            maxY = Math.max(maxY, node.y);
          }
        });

        // 如果无法获取节点坐标，使用默认缩放并居中
        if (!isFinite(minX) || !isFinite(maxX) || !isFinite(minY) || !isFinite(maxY)) {
          console.log('无法获取节点坐标，使用默认缩放并居中');
          chart.setOption({
            series: [{
              zoom: 0.8,
              center: ['50%', '50%']
            }]
          });
          return;
        }

        // 获取图表容器大小
        const containerSize = chart.getWidth && chart.getHeight ?
            { width: chart.getWidth(), height: chart.getHeight() } :
            { width: 750, height: 400 }; // 默认尺寸

        // 计算图形的实际大小
        const graphWidth = maxX - minX;
        const graphHeight = maxY - minY;

        // 添加一些边距
        const padding = 80;
        const availableWidth = containerSize.width - padding * 2;
        const availableHeight = containerSize.height - padding * 2;

        // 计算适合的缩放比例
        const scaleX = availableWidth / graphWidth;
        const scaleY = availableHeight / graphHeight;
        const scale = Math.min(scaleX, scaleY, 1); // 不放大，只缩小

        // 应用缩放和居中
        if (scale < 0.95) { // 需要缩小时
          chart.setOption({
            series: [{
              zoom: Math.max(scale, 0.3), // 不小于最小缩放
              center: ['50%', '50%']
            }]
          });
          console.log('应用自动缩放, scale:', Math.max(scale, 0.3));
        } else {
          // 即使不需要缩放，也要确保居中
          chart.setOption({
            series: [{
              center: ['50%', '50%']
            }]
          });
          // 减少冗余日志
        }

      } catch (error) {
        console.log('执行自动适配失败:', error);
      }
    },

    toggleRelation(type) {
      const index = this.selectedRelations.indexOf(type);
      let newSelectedRelations = [...this.selectedRelations];

      if (index === -1) {
        // 添加这个关系类型
        newSelectedRelations.push(type);
      } else {
        // 移除这个关系类型
        newSelectedRelations.splice(index, 1);
        // 如果所有关系都被取消，重新选择所有关系
        if (newSelectedRelations.length === 0) {
          newSelectedRelations = this.apiRelationTypes.map(t => t.type);
        }
      }

      // 直接替换数组确保响应式更新
      this.selectedRelations = newSelectedRelations;

      // watcher 会自动调用 updateChart，这里不需要手动调用
    },
    getRelationColor(type) {
      const relType = this.relationTypesMap[type];
      return relType ? relType.color : '#8B5CF6';
    },
    
    // 获取节点符号
    getNodeSymbol(node) {
      // 所有节点都使用圆形
      return 'circle';
    },

    // 创建圆形头像符号
    async createCircleAvatarSymbol(avatarUrl, size) {
      try {
        // 先将图片转换为canvas，然后创建圆形头像
        return await this.createCircleAvatarCanvas(avatarUrl, size);
      } catch (e) {
        console.warn('创建圆形头像失败，使用普通头像:', e);
        return `image://${avatarUrl}`;
      }
    },

    // 使用Canvas创建圆形头像
    createCircleAvatarCanvas(avatarUrl, size) {
      return new Promise((resolve, reject) => {
        // #ifdef H5
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const img = new Image();
        
        canvas.width = size;
        canvas.height = size;
        
        img.crossOrigin = 'anonymous'; // 处理跨域问题
        img.onload = () => {
          // 保存当前状态
          ctx.save();
          
          // 创建圆形裁剪路径
          ctx.beginPath();
          ctx.arc(size / 2, size / 2, size / 2 - 2, 0, Math.PI * 2);
          ctx.clip();
          
          // 绘制头像图片
          ctx.drawImage(img, 0, 0, size, size);
          
          // 恢复状态并添加圆形边框
          ctx.restore();
          ctx.beginPath();
          ctx.arc(size / 2, size / 2, size / 2 - 1, 0, Math.PI * 2);
          ctx.strokeStyle = '#fff';
          ctx.lineWidth = 2;
          ctx.stroke();
          
          // 转换为data URL
          const dataUrl = canvas.toDataURL('image/png');
          resolve(`image://${dataUrl}`);
        };
        
        img.onerror = () => {
          reject(new Error('头像加载失败'));
        };
        
        img.src = avatarUrl;
        // #endif
        
        // #ifdef MP
        // 小程序环境直接使用原图片URL
        resolve(`image://${avatarUrl}`);
        // #endif
      });
    },

    // 处理所有需要头像符号的节点 - 优化版本
    async processAvatarSymbols() {
      // 拖拽进行中时跳过头像处理，提升性能
      if (this.isDragInProgress) {
        console.log('拖拽进行中，跳过头像处理');
        return;
      }

      // 获取当前图表选项
      const option = this.chartOption;
      if (!option || !option.series || !option.series[0] || !option.series[0].data) {
        return;
      }

      const nodes = option.series[0].data;
      const avatarPromises = [];
      
      // 初始化头像缓存
      if (!this.avatarCache) {
        this.avatarCache = new Map();
      }

      // 收集所有需要异步处理头像的节点
      nodes.forEach((node, index) => {
        if (node._needAvatarSymbol && node._avatarUrl) {
          // 检查缓存
          const cacheKey = `${node._avatarUrl}-${node._nodeSize}`;
          if (this.avatarCache.has(cacheKey)) {
            // 使用缓存的头像符号
            nodes[index].symbol = this.avatarCache.get(cacheKey);
            delete nodes[index]._needAvatarSymbol;
            delete nodes[index]._avatarUrl;
            delete nodes[index]._nodeSize;
            return;
          }

          const promise = this.createCircleAvatarSymbol(node._avatarUrl, node._nodeSize)
            .then(symbol => {
              // 缓存头像符号
              this.avatarCache.set(cacheKey, symbol);
              // 更新节点符号
              nodes[index].symbol = symbol;
              // 清理标记字段
              delete nodes[index]._needAvatarSymbol;
              delete nodes[index]._avatarUrl;
              delete nodes[index]._nodeSize;
            })
            .catch(error => {
              console.warn(`节点 ${node.name || node.id} 的头像处理失败:`, error);
              // 出错时保持圆形符号
              const fallbackSymbol = 'circle';
              this.avatarCache.set(cacheKey, fallbackSymbol);
              nodes[index].symbol = fallbackSymbol;
              delete nodes[index]._needAvatarSymbol;
              delete nodes[index]._avatarUrl;
              delete nodes[index]._nodeSize;
            });
          
          avatarPromises.push(promise);
        }
      });

      // 批量处理头像，限制并发数量避免性能问题
      if (avatarPromises.length > 0) {
        console.log(`处理 ${avatarPromises.length} 个头像符号...`);
        
        // 分批处理，每批最多5个
        const batchSize = 5;
        for (let i = 0; i < avatarPromises.length; i += batchSize) {
          const batch = avatarPromises.slice(i, i + batchSize);
          await Promise.all(batch);
          
          // 给浏览器一点时间处理其他任务
          if (i + batchSize < avatarPromises.length) {
            await new Promise(resolve => setTimeout(resolve, 10));
          }
        }
        
        console.log('所有头像符号处理完成');
      }
    },

    


    // 处理保存图片事件
    onSaveImage() {
      console.log('保存图片操作被触发');
    },

    autoLayoutNodes(nodes, centerX = 350, centerY = 250, radius = 300) {
      const n = nodes.length;
      return nodes.map((node, i) => {
        if (typeof node.x === 'number' && typeof node.y === 'number') return node;
        const angle = (2 * Math.PI * i) / n;
        return {
          ...node,
          x: Math.round(centerX + radius * Math.cos(angle)),
          y: Math.round(centerY + radius * Math.sin(angle))
        };
      });
    },
    async saveLayout() {
      if (!this.chartInstance) return;

      try {
        const graph = this.chartInstance.getModel().getSeriesByIndex(0).getGraph();
        const nodes = graph.nodes.map(node => {
          const model = node.getModel();
          const [x, y] = node.getLayout();
          return {
            ...model.option,
            x,
            y
          };
        });

        const layoutJson = JSON.stringify(nodes);

        // 保存到本地存储
        safeStorage.setItem('graphLayout', layoutJson);

        // 保存到服务器 - 使用通用设置接口
        await CommApi.saveUserSetting({
          settingType: 'graph_layout',
          settingValue: layoutJson
        });

        // 确保切换到none布局模式
        this.layoutMode = 'none';

        // 立即更新 apiNodes 的坐标，确保后续操作不会丢失布局
        this.updateApiNodesWithCurrentPositions(nodes);

        uni.showToast({ title: '布局已保存到云端' });
      } catch (error) {
        console.log('保存布局失败:', error);
        uni.showToast({ title: '保存失败', icon: 'error' });
      }
    },

    // 更新 apiNodes 的坐标
    updateApiNodesWithCurrentPositions(layoutNodes) {
      this.apiNodes = this.apiNodes.map(node => {
        const layoutNode = layoutNodes.find(n => n.id == node.id);
        if (layoutNode) {
          return { ...node, x: layoutNode.x, y: layoutNode.y };
        }
        return node;
      });
    },
    onChartReady() {
      console.log('图表ready事件触发');
      const chartComponent = this.$refs.chartRef;
      if (chartComponent && chartComponent.setOption) {
        // 如果已经初始化完成，避免重复设置
        if (!this.initializationComplete) {
        chartComponent.setOption(this.chartOption);
        }
        this.chartLoading = false;
      } else if (!this.hasFetchedData) {
        // 只有在没有获取过数据时才调用updateChart
        this.updateChart();
      }
    },
    onPullDownRefresh() {
      // 只在"通讯录"tab（tabIndex === 0）时刷新
      if (this.tabIndex === 0) {
        this.fetchContactList();
      } else {
        // 其它tab直接停止刷新动画
        uni.stopPullDownRefresh();
      }
    },
    onLoad() {
      // 只有在独立页面使用时才计算高度（可通过是否有父组件的特定标识来判断）
      // 当作为子组件使用时，应该依赖父容器的CSS高度设置
      if (this.$parent && this.$parent.$options.name === 'RelationGraphPage') {
        // 独立页面使用
      if (process.env.UNI_PLATFORM === 'h5') {
        // H5端：考虑header、筛选区域等高度
        const headerHeight = 44;
        const tabbarHeight = 56;
        const controlsHeight = 100; // 筛选图例和按钮区域
        this.chartHeight = Math.max(400, window.innerHeight - headerHeight - tabbarHeight - controlsHeight);
      } else {
        // 小程序端
        uni.getSystemInfo({
          success: res => {
            const tabbarHeight = 50;
            const controlsHeight = 100; // 筛选图例和按钮区域
            this.chartHeight = Math.max(400, res.windowHeight - tabbarHeight - controlsHeight);
          }
        })
        }
      } else {
        // 作为子组件使用时，依赖父容器CSS高度
        console.log('关系图组件作为子组件使用，依赖父容器高度设置');
      }
    },
    // 显示关系维护弹窗
    showRelationMaintain() {
      uni.navigateTo({
        url: '/pages/contact/relation-maintain',
        events: {
          // 监听关系更新事件
          relationUpdated: () => {
            // 刷新关系图
            this.refreshRelationGraph();
          }
        }
      });
    },

    // 刷新关系图数据
    refreshRelationGraph() {
      this.chartLoading = true;
      this.chartKey++;

      // 清理旧的图表实例引用，避免使用已销毁的实例
      if (this.chartInstance) {
        console.log('清理旧的图表实例引用');
        this.chartInstance = null;
      }

      // 注意：不要重置hasExistingLayout，保留本地布局优先级
      this.fetchGraphData();
    },
    async fetchGraphData() {
      // 防止重复调用
      if (this.isInitializing && this.hasFetchedData) {
        console.log('数据正在获取中或已获取，跳过重复调用');
        return;
      }

      try {
        console.log('开始获取关系图数据...');
        const res = await RelationApi.queryRelationGraph();

        if (res.code === 0 && res.data) {
          let nodes = res.data.nodes || [];

          let savedLayout = safeStorage.getItem('graphLayout');

          if (savedLayout) {
            try {
              const layoutNodes = JSON.parse(savedLayout);

              let appliedCount = 0;
              nodes = nodes.map(node => {
                const layoutNode = layoutNodes.find(n => n.id == node.id);
                if (layoutNode) {
                  appliedCount++;
                  return { ...node, x: layoutNode.x, y: layoutNode.y };
                }
                return node;
              });

              this.layoutMode = 'none';
              this.hasExistingLayout = true;
              console.log('使用本地保存的布局数据');
            } catch (e) {
              // 本地布局数据解析失败，使用force布局
              nodes = nodes.map(node => {
                const n = { ...node };
                delete n.x;
                delete n.y;
                return n;
              });
              this.layoutMode = 'force';
              this.hasExistingLayout = false;
              console.log('本地布局数据解析失败，使用force布局', e);
            }
          } else {
            // 本地无布局，查询服务器 - 使用通用设置接口
            const layoutRes = await CommApi.queryUserSetting({
              settingType: 'graph_layout'
            });

            if (layoutRes.code === 0 && layoutRes.data && layoutRes.data.settingValue) {
              try {
                const layoutNodes = JSON.parse(layoutRes.data.settingValue);
                nodes = nodes.map(node => {
                  const layoutNode = layoutNodes.find(n => n.id == node.id);
                  return layoutNode ? { ...node, x: layoutNode.x, y: layoutNode.y } : node;
                });
                this.layoutMode = 'none';
                this.hasExistingLayout = true;
                console.log('使用服务器保存的布局数据');
              } catch (e) {
                // 服务器布局数据解析失败，使用force布局
                nodes = nodes.map(node => {
                  const n = { ...node };
                  delete n.x;
                  delete n.y;
                  return n;
                });
                this.layoutMode = 'force';
                this.hasExistingLayout = false;
                console.log('服务器布局数据解析失败，使用force布局');
              }
            } else {
              // 接口也无布局，依赖 force 自动布局
              nodes = nodes.map(node => {
                const n = { ...node };
                delete n.x;
                delete n.y;
                return n;
              });
              this.layoutMode = 'force';
              this.hasExistingLayout = false;
              console.log('无任何布局数据，使用force布局（首次进入）');
            }
          }

          this.apiNodes = nodes;
          this.apiEdges = res.data.edges || [];
          this.apiRelationTypes = res.data.relationTypes || [];

          // 确保 selectedRelations 正确初始化
          const relationTypes = this.apiRelationTypes || [];
          this.selectedRelations = relationTypes.map(t => t.type);

          this.chartLoading = false;
          this.updateChart();

          // 数据加载完成后，检查是否需要自动切换布局
          this.$nextTick(() => {
            if (this.chartInstance) {
              this.autoSwitchToNoneLayout(this.chartInstance);
            }
          });

          // 更新最后更新时间
          this.lastUpdateTime = Date.now();
          // 小程序端持久化lastUpdateTime，便于页面切换后恢复
          try {
            uni.setStorageSync && uni.setStorageSync('graphLastUpdateTime', this.lastUpdateTime);
            console.log('[fetchGraphData] setStorageSync graphLastUpdateTime:', this.lastUpdateTime);
          } catch (e) {
            console.warn('[fetchGraphData] setStorageSync失败', e);
          }
        } else {
          // API 返回但无数据
          console.log('API返回无数据或错误');
          this.apiNodes = [];
          this.apiEdges = [];
          this.apiRelationTypes = [];
          this.selectedRelations = [];
          this.chartLoading = false;
        }
      } catch (error) {
        console.error('fetchGraphData 异常:', error);
        // 异常时也要清空数据并停止加载
        this.apiNodes = [];
        this.apiEdges = [];
        this.apiRelationTypes = [];
        this.selectedRelations = [];
        this.chartLoading = false;
      }
    },

    // 供父页面调用的刷新方法
    refresh() {
      const now = Date.now();
      console.log('[refresh] called, lastUpdateTime:', this.lastUpdateTime, 'now:', now, 'diff:', now - this.lastUpdateTime);
      // lastUpdateTime为0时直接允许刷新
      if (this.lastUpdateTime === 0 || (now - this.lastUpdateTime) > 1000) {
        console.log('[refresh] 满足刷新条件，调用refreshRelationGraph');
        uni.showToast({ title: '正在刷新关系图...', icon: 'none' });
        this.refreshRelationGraph();
      } else {
        const remain = Math.ceil((5000 - (now - this.lastUpdateTime)) / 1000);
        // uni.showToast({ title: `距离上次刷新需等待${remain > 0 ? remain : 1}秒`, icon: 'none' });
        console.log('[refresh] 距离上次更新时间不足1秒，无需刷新');
      }
    },


    handleFilterApply(filterValues) {
      this.filterValues = { ...filterValues };
      this.updateChart();
      this.showFilterDrawer = false;
    },
    handleFilterReset() {
      this.filterValues = {};
      this.updateChart();
      this.showFilterDrawer = false;
    },
    handleSettingApply(setting) {
      this.setting = { ...setting };
      this.updateChart();
      this.showSettingDrawer = false;
    },
    


    // 加载用户设置
    async loadUserSettings() {
      try {
        // 先尝试从本地存储加载
        const localSetting = safeStorage.getItem('graphSetting');
        if (localSetting) {
          try {
            const parsedSetting = JSON.parse(localSetting);
            this.setting = { ...this.setting, ...parsedSetting };
            console.log('使用本地保存的图表设置');
            return;
          } catch (e) {
            console.log('本地设置数据解析失败');
          }
        }

        // 本地无设置，查询服务器
        const settingRes = await CommApi.queryUserSetting({
          settingType: 'graph_setting'
        });

        if (settingRes.code === 0 && settingRes.data && settingRes.data.settingValue) {
          try {
            const parsedSetting = JSON.parse(settingRes.data.settingValue);
            this.setting = { ...this.setting, ...parsedSetting };
            // 同时保存到本地
            safeStorage.setItem('graphSetting', settingRes.data.settingValue);
            console.log('使用服务器保存的图表设置');
          } catch (e) {
            console.log('服务器设置数据解析失败，使用默认设置');
          }
        } else {
          console.log('无用户设置数据，使用默认设置');
        }
      } catch (error) {
        console.log('加载用户设置失败:', error);
      }
    },
  },
  created() {
    // 只加载用户设置，不获取数据
    this.loadUserSettings();
  },
  mounted() {
    this.$nextTick(() => {
      console.log('relation-graph 组件已挂载');
      // 打印canvas实际宽高
      // #ifdef H5
      const canvas = document.querySelector('canvas');
      console.log('canvas宽高:', canvas?.width, canvas?.height);
      // #endif
      
      // 统一在mounted中初始化
      this.initializeComponent();
    });
  },
  beforeDestroy() {
    // 清理所有定时器
    this.clearAllTimers();
  },
  // Vue 3 兼容
  beforeUnmount() {
    // 清理所有定时器
    this.clearAllTimers();
  },
  // 增加onShow钩子，页面显示时同步lastUpdateTime（小程序端）
  onShow() {
    try {
      // #ifdef MP-WEIXIN
      const last = uni.getStorageSync && uni.getStorageSync('graphLastUpdateTime');
      if (last) {
        this.lastUpdateTime = last;
        console.log('[onShow] 恢复lastUpdateTime:', last);
      } else {
        console.log('[onShow] 未获取到本地lastUpdateTime');
      }
      // #endif
    } catch (e) {
      console.warn('[onShow] getStorageSync失败', e);
    }
  },
}
</script>

<style lang="scss">
.relation-graph-container {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  max-height: 100%; /* 防止超出父容器 */
  position: relative;
  background: transparent;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* 微信小程序专用优化 */
/* #ifdef MP-WEIXIN */
.relation-graph-container {
  /* 确保在微信小程序中严格继承父容器高度 */
  box-sizing: border-box;
}
/* #endif */
.relation-map {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  background: transparent;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.relation-graph-content {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  background: transparent;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.relation-graph-content {
  position: relative;
  width: 100%;
  height: 100%;
  max-height: 100%; /* 防止内容区域超出 */
  background-color: #ffffff;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;

  :deep(.lime-echart) {
    width: 100%;
    height: 100%;
    max-height: 100%; /* 确保图表不超出容器 */
    background: transparent !important;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1; /* 确保图表能填充剩余空间 */
    box-sizing: border-box;
    padding-top: 40px; /* 为浮动的图例预留空间 */
  }

  // 确保canvas居中显示
  :deep(canvas) {
    margin: 0 auto;
    display: block;
  }



  .relation-legend {
    position: absolute; /* 浮动在图表上方 */
    top: 0;
    right: 0;
    left: 0;
    z-index: 10002; /* 确保在图表上方 */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end; // 改为右对齐
    padding: 8px 10px 4px 10px; // 调整内边距
    background: rgba(255, 255, 255, 0.85); /* 半透明背景 */
    backdrop-filter: blur(4px); /* 背景模糊效果 */
    border-bottom: 1px solid rgba(0, 0, 0, 0.05); /* 添加底部边框 */
    row-gap: 6px; // 减少行间距
    .legend-item {
      display: flex;
      align-items: center;
      margin-right: 10px; // 进一步减小标签间距，从12px改为10px
      margin-bottom: 2px; // 减少底部边距，从4px改为2px
      cursor: pointer;
      opacity: 0.6;
      transition: opacity 0.2s;
      &.active {
        opacity: 1;
        font-weight: bold;
      }
      .legend-dot {
        width: 12px;
        height: 12px;
        border-radius: 50%;
        margin-right: 6px;
        border: 2px solid #fff;
        box-shadow: 0 0 2px rgba(0,0,0,0.08);
      }
      .legend-label {
        font-size: 14px;
        color: #333;
      }
    }
  }

  .zoom-controls {
    position: fixed;
    right: 20px;
    bottom: 140px;  // 相对底部定位，在维护关系按钮上方
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 9999;
    background: rgba(255, 255, 255, 0.9);
    padding: 8px;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  }

  .chart-loading-mask {
    position: fixed;
    left: 0; top: 0; right: 0; bottom: 0;
    background: #fff;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    color: #8B5CF6;
  }

  .empty-data-tip {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    padding: 40px 20px;
    z-index: 10001;

    .empty-icon {
      font-size: 48px;
      margin-bottom: 16px;
    }

    .empty-title {
      font-size: 18px;
      font-weight: 600;
      color: #333;
      margin-bottom: 8px;
    }

    .empty-desc {
      font-size: 14px;
      color: #666;
      line-height: 1.5;
      margin-bottom: 24px;
      max-width: 280px;
    }

    .empty-action-btn {
      background: #2563eb;
      color: #fff;
      border: none;
      border-radius: 20px;
      padding: 8px 24px;
      font-size: 14px;
      font-weight: 500;
      box-shadow: 0 2px 8px rgba(37,99,235,0.3);
      transition: all 0.2s;

      &:active {
        transform: scale(0.96);
        box-shadow: 0 1px 4px rgba(37,99,235,0.2);
      }
    }
  }

  .bottom-actions {
    position: fixed;
    left: 50%;
    bottom: 60px; /* 浮动在tabbar上方，紧贴tabbar边界 */
    transform: translateX(-50%);
    z-index: 10001;
    display: flex;
    flex-direction: row;
    gap: 8px; /* 减少按钮间距 */
    background: rgba(255, 255, 255, 0.9); /* 增加背景透明度，避免与tabbar冲突 */
    padding: 3px 8px; /* 减少内边距 */
    border-radius: 14px; /* 稍微减少圆角 */
    box-shadow: 0 2px 12px rgba(0,0,0,0.12); /* 增强阴影，突出浮动效果 */
    backdrop-filter: blur(8px); /* 添加背景模糊效果 */
  }

  .action-btn {
    min-width: 60px;
    padding: 4px 10px;
    background: #2563eb;
    color: #fff;
    border: none;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 500;
    box-shadow: 0 1px 4px rgba(37,99,235,0.10);
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    line-height: 1.2;
    letter-spacing: 0.5px;
    &:active {
      transform: scale(0.96);
      box-shadow: 0 1px 2px rgba(37,99,235,0.08);
    }
  }
}

.legend-style {
  display: flex;
  flex-direction: row;
  justify-content: center;
  margin-bottom: 10px;
  .filter-item {
    border: 1px solid;
    border-radius: 16px;
    padding: 4px 12px;
    margin: 0 4px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.2s;
    &.active {
      font-weight: bold;
      background: #e0e7ff;
    }
  }
}

html, body, #app {
  height: 100%;
}

// 新增：抽屉样式参考京东App
.drawer-content {
  width: 80vw;
  max-width: 420px;
  min-width: 280px;
  background: #fff;
  border-radius: 18px 0 0 18px;
  box-shadow: -4px 0 24px rgba(0,0,0,0.10);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding-bottom: 80px;
  position: relative;
}
.drawer-title {
  font-size: 18px;
  font-weight: 600;
  padding: 22px 24px 10px 24px;
  border-bottom: 1px solid #f2f2f2;
  margin-bottom: 8px;
}
.drawer-item {
  margin: 0 24px 18px 24px;
  display: flex;
  flex-direction: column;
}
.drawer-label {
  font-size: 15px;
  font-weight: 500;
  margin-bottom: 10px;
  color: #222;
}

// 底部操作区
.drawer-actions {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 80vw;
  max-width: 420px;
  min-width: 280px;
  background: #fff;
  border-top: 1px solid #f2f2f2;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  padding: 14px 12px 18px 12px;
  z-index: 10010;
  box-sizing: border-box;
}
.drawer-actions button {
  flex: 1;
  margin: 0 2px;
  padding: 6px 8px;
  border-radius: 14px;
  font-size: 14px;
  font-weight: 500;
  border: none;
  background: #f5f6fa;
  color: #222;
  transition: all 0.2s;
  min-height: 28px;
  max-width: calc(50% - 4px);
}
.drawer-actions button:last-child {
  background: #2563eb;
  color: #fff;
  box-shadow: 0 2px 8px rgba(37,99,235,0.10);
}
.drawer-actions.small {
  padding: 4px 6px 8px 6px;
  border-top: 1px solid #f2f2f2;
}
// 开关控件样式
.switch-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.switch-desc {
  font-size: 13px;
  color: #666;
  flex: 1;
}
switch {
  transform: scale(0.8);
}

.drawer-actions.small button,
.drawer-actions.small .mini-btn {
  flex: 1;
  margin: 0 2px;
  padding: 4px 0;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 500;
  border: none;
  background: #f5f6fa;
  color: #222;
  transition: all 0.2s;
}
.drawer-actions.small button:last-child,
.drawer-actions.small .mini-btn:last-child {
  background: #2563eb;
  color: #fff;
  box-shadow: 0 2px 8px rgba(37,99,235,0.10);
}

</style> 